package com.example.musicplayer.dialogTools;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;

import com.example.musicplayer.MusicPlayerApplication;
import com.example.musicplayer.R;
import com.example.musicplayer.commonUtils.DisplayUtil;
import com.example.musicplayer.user.User;

import java.util.ArrayList;
import java.util.List;

public class ImageDisplayDialog extends BaseDialog implements View.OnClickListener {
    private static final int DRAG_X_TO_DISMISS = 500;
    private static final int DRAG_Y_TO_DISMISS = 600;
    private static final int MIN_DISTANCE_TO_DRAG = 5;
    private static final float MIN_RATIO_TO_FLIP = 0.3f;
    //get the image and set
    private static final int UPDATE_IMAGE_LEFT = 1;
    private static final int UPDATE_IMAGE_CENTER = 2;
    private static final int UPDATE_IMAGE_RIGHT = 3;
    //response click event
    private static final int MODE_CLICK = 1;
    //drag the image
    private static final int MODE_DRAG = 2;
    //turn to next or last image
    private static final int MODE_FLIP = 3;
    private final int SCREEN_WIDTH;
    private int mMode;
    private int mIndex;
    private boolean mLocked = false;
    private List<Integer> mImageList = new ArrayList<Integer>();
    //three loaded image view
    private ImageViewInfo mLeft;
    private ImageViewInfo mCenter;
    private ImageViewInfo mRight;
    private final MyHandler mHandler;
    private GestureDetector mDetector;
    private DialogTouchListener mTouchListener;
    private ConfirmListener mConfirmListener;

    private class DialogTouchListener implements View.OnTouchListener {
        private int mStartX;
        private int mStartY;
        private boolean mLongPress = false;
        private boolean mShouldFlip = false;

        public void setLongPress(boolean isLongPress) {
            mLongPress = isLongPress;
        }

        public void setShouldFlip(boolean shouldFlip) {
            mShouldFlip = shouldFlip;
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            //gesture listener
            mDetector.onTouchEvent(event);
            //location
            int x = (int) event.getRawX();
            int y = (int) event.getRawY();
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mStartX = x;
                    mStartY = y;
                    mMode = MODE_CLICK;
                    mLongPress = false;
                    mShouldFlip = false;
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (mMode != MODE_CLICK) {
                        updateImageLayout(x - mStartX, y - mStartY);
                        updateImageSize(x - mStartX, y - mStartY);
                    } else {
                        if (Math.abs(x - mStartX) >= MIN_DISTANCE_TO_DRAG) {
                            if (mImageList.size() > 1) {
                                mMode = MODE_FLIP;
                            } else {
                                mMode = MODE_DRAG;
                                setPanelVisible(false);
                            }
                        } else if (Math.abs(y - mStartY) >= MIN_DISTANCE_TO_DRAG) {
                            mMode = MODE_DRAG;
                            setPanelVisible(false);
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (mMode == MODE_FLIP && mShouldFlip) {
                        int dx = x - mStartX;
                        //the border judgment
                        if ((dx > 0 && mIndex >= 1) || (dx < 0 && mIndex < mImageList.size() - 1)) {
                            flipToNext(x - mStartX);
                        }
                        break;
                    }
                    if ((mMode == MODE_CLICK || Math.abs(x - mStartX) >= DRAG_X_TO_DISMISS
                            || Math.abs(y - mStartY) >= DRAG_Y_TO_DISMISS) && mMode != MODE_FLIP && !mLongPress && !mLocked) {
                        mCenter.mImageView.performClick();
                    } else {
                        int dx = x - mStartX;
                        if (mMode == MODE_FLIP && mCenter.mWidth * MIN_RATIO_TO_FLIP <= Math.abs(dx)
                                && ((dx > 0 && mIndex >= 1) || (dx < 0 && mIndex < mImageList.size() - 1))) {
                            flipToNext(x - mStartX);
                        } else {
                            updateImageLayout(0, 0);
                            updateImageSize(0, 0);
                        }
                    }
                    setPanelVisible(true);
                    break;
                case MotionEvent.ACTION_CANCEL:
                    updateImageLayout(0, 0);
                    updateImageSize(0, 0);
                    setPanelVisible(true);
                    break;
            }
            return true;
        }
    }

    private static class ImageViewInfo {
        public ImageView mImageView;
        public int mHeight;
        public int mWidth;
        public FrameLayout.LayoutParams mParams;

        public ImageViewInfo() {
            mHeight = 0;
            mWidth = 0;
            mParams = new FrameLayout.LayoutParams(0, 0);
            mParams.gravity = Gravity.CENTER;
        }
    }

    @SuppressLint("HandlerLeak")
    private class MyHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.obj instanceof Bitmap) {
                Bitmap bitmap = (Bitmap) msg.obj;
                float width = bitmap.getWidth();
                float height = bitmap.getHeight();
                int imageHeight = (int) (height / width * SCREEN_WIDTH);
                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(SCREEN_WIDTH, imageHeight);
                params.gravity = Gravity.CENTER;
                ImageViewInfo imageViewInfo = null;
                switch (msg.what) {
                    case UPDATE_IMAGE_LEFT:
                        imageViewInfo = mLeft;
                        params.leftMargin = -SCREEN_WIDTH;
                        break;
                    case UPDATE_IMAGE_CENTER:
                        imageViewInfo = mCenter;
                        params.leftMargin = 0;
                        break;
                    case UPDATE_IMAGE_RIGHT:
                        imageViewInfo = mRight;
                        params.leftMargin = SCREEN_WIDTH;
                        break;
                }
                if (imageViewInfo != null) {
                    imageViewInfo.mParams = params;
                    imageViewInfo.mWidth = SCREEN_WIDTH;
                    imageViewInfo.mHeight = imageHeight;
                    imageViewInfo.mImageView.setPadding(10, 10, 10, 10);
                    imageViewInfo.mImageView.setLayoutParams(params);
                    imageViewInfo.mImageView.setImageBitmap(bitmap);
                }
            }
        }
    }

    public interface ConfirmListener {
        void onConfirm(int position);
    }

    public ImageDisplayDialog(Context context) {
        super(context, R.style.FullDialog, R.style.FullDialog_NoAnim);
        mDialog.setCanceledOnTouchOutside(false);
        SCREEN_WIDTH = DisplayUtil.getScreenSize(MusicPlayerApplication.getInstance().getTopActivity())[0];
        mHandler = new MyHandler();
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected View setDialogView(Context context) {
        View view = View.inflate(context, R.layout.dialog_display_image, null);
        mLeft = new ImageViewInfo();
        mLeft.mImageView = view.findViewById(R.id.image_display_left);
        mCenter = new ImageViewInfo();
        mCenter.mImageView = view.findViewById(R.id.image_display_center);
        mRight = new ImageViewInfo();
        mRight.mImageView = view.findViewById(R.id.image_display_right);
        mLeft.mImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });
        mRight.mImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });
        mCenter.mImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });
        //touch listener
        mTouchListener = new DialogTouchListener();
        mLeft.mImageView.setOnTouchListener(mTouchListener);
        mRight.mImageView.setOnTouchListener(mTouchListener);
        mCenter.mImageView.setOnTouchListener(mTouchListener);
        view.findViewById(R.id.dialog_container).setOnTouchListener(mTouchListener);
        view.findViewById(R.id.confirm).setOnClickListener(this);
        view.findViewById(R.id.lock).setOnClickListener(this);
        mDetector = new GestureDetector(context, new GestureDetector.OnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) {
                return false;
            }

            @Override
            public void onShowPress(MotionEvent e) {

            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return false;
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                return false;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                if (mLocked) {
                    lockOrNot(false);
                    mTouchListener.setLongPress(true);
                }
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                if (Math.abs(velocityX) >= 1000 && mMode == MODE_FLIP) {
                    mTouchListener.setShouldFlip(true);
                }
                return false;
            }
        });
        return view;
    }

    @Override
    protected void setDialogLayout(Context context) {
        final Window dialogWindow = mDialog.getWindow();
        if (dialogWindow != null) {
            dialogWindow.setDimAmount(1.0f);
        }
    }

    private void updateImageSize(int x, int y) {
        if (mMode == MODE_DRAG) {
            float ratioX = 1 - Math.abs((float) x) / 2000f;
            float ratioY = 1 - Math.abs((float) y) / 2000f;
            float ratio = Math.min(ratioX, ratioY);
            if (ratio >= 0.1f) {
                mCenter.mParams.width = (int) (mCenter.mWidth * ratio);
                mCenter.mParams.height = (int) (mCenter.mHeight * ratio);
                mCenter.mImageView.setLayoutParams(mCenter.mParams);
            }
        }
    }

    private void updateImageLayout(int x, int y) {
        if (mMode != MODE_CLICK) {
            if ((x >= 0 && mIndex == 0) || (x < 0 && mIndex == mImageList.size() - 1)) {
                mCenter.mParams.leftMargin = 0;
            } else {
                mCenter.mParams.leftMargin = x;
            }
            if (mMode == MODE_DRAG) {
                mCenter.mParams.leftMargin = x;
            } else {
                y = 0;
            }
            mCenter.mParams.topMargin = y;
            if (mMode == MODE_FLIP) {
                if (x >= 0) {
                    if (mIndex - 1 >= 0) {
                        mLeft.mParams.leftMargin = x - SCREEN_WIDTH;
                    } else {
                        mLeft.mParams.leftMargin = -SCREEN_WIDTH;
                    }
                    mRight.mParams.leftMargin = SCREEN_WIDTH;
                } else {
                    if (mIndex + 1 < mImageList.size()) {
                        mRight.mParams.leftMargin = x + SCREEN_WIDTH;
                    } else {
                        mRight.mParams.leftMargin = SCREEN_WIDTH;
                    }
                    mLeft.mParams.leftMargin = -SCREEN_WIDTH;
                }
                mLeft.mParams.topMargin = y;
                mRight.mParams.topMargin = y;
            }
            mCenter.mImageView.setLayoutParams(mCenter.mParams);
            mLeft.mImageView.setLayoutParams(mLeft.mParams);
            mRight.mImageView.setLayoutParams(mRight.mParams);
            //set dim alpha
            if (mMode == MODE_DRAG) {
                float alphaX = 1 - Math.abs((float) x) / 1000f;
                float alphaY = 1 - Math.abs((float) y) / 1000f;
                mDialog.getWindow().setDimAmount(Math.max(0f, Math.min(alphaX, alphaY)));
            }
        }
    }

    private void updateImageBitmap(final int what) {
        int index;
        switch (what) {
            case UPDATE_IMAGE_LEFT:
                index = mIndex - 1;
                break;
            case UPDATE_IMAGE_CENTER:
                index = mIndex;
                break;
            case UPDATE_IMAGE_RIGHT:
                index = mIndex + 1;
                break;
            default:
                index = -1;
                break;
        }
        if (index >= 0 && index < mImageList.size()) {
            final int bitmapId = mImageList.get(index);
            new Thread() {
                @Override
                public void run() {
                    Bitmap bitmap = User.getUserPhoto(bitmapId);
                    Message msg = mHandler.obtainMessage(what);
                    msg.obj = bitmap;
                    mHandler.sendMessage(msg);
                }
            }.start();
        }
    }

    private void flipToNext(final int dx) {
        TranslateAnimation animation_center;
        TranslateAnimation animation_side;
        if (dx > 0) {
            animation_center = new TranslateAnimation(0, SCREEN_WIDTH - Math.abs(dx), 0, 0);
            animation_side = new TranslateAnimation(0, SCREEN_WIDTH - Math.abs(dx), 0, 0);
        } else {
            animation_center = new TranslateAnimation(0, Math.abs(dx) - SCREEN_WIDTH, 0, 0);
            animation_side = new TranslateAnimation(0, Math.abs(dx) - SCREEN_WIDTH, 0, 0);
        }
        animation_center.setDuration(200);
        animation_side.setDuration(200);
        animation_center.setFillAfter(true);
        animation_side.setFillAfter(true);
        animation_side.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                if (dx > 0) {
                    mCenter.mParams.leftMargin = -SCREEN_WIDTH;
                    mLeft.mParams.leftMargin = 0;
                } else {
                    mCenter.mParams.leftMargin = SCREEN_WIDTH;
                    mRight.mParams.leftMargin = 0;
                }
                mCenter.mImageView.setLayoutParams(mCenter.mParams);
                mLeft.mImageView.setLayoutParams(mLeft.mParams);
                mRight.mImageView.setLayoutParams(mRight.mParams);
                ImageViewInfo temp = mCenter;
                if (dx > 0) {
                    mCenter = mLeft;
                    mLeft = temp;
                    mIndex--;
                } else {
                    mCenter = mRight;
                    mRight = temp;
                    mIndex++;
                }
                updateImageBitmap(UPDATE_IMAGE_LEFT);
                updateImageBitmap(UPDATE_IMAGE_CENTER);
                updateImageBitmap(UPDATE_IMAGE_RIGHT);
                //clear animation
                mCenter.mImageView.clearAnimation();
                mLeft.mImageView.clearAnimation();
                mRight.mImageView.clearAnimation();
                //update index view
                updateCurrentPositionView();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        mCenter.mImageView.startAnimation(animation_center);
        if (dx > 0) {
            mLeft.mImageView.startAnimation(animation_side);
        } else {
            mRight.mImageView.startAnimation(animation_side);
        }
    }

    private void updateCurrentPositionView() {
        TextView textView = mDialogView.findViewById(R.id.position);
        String text = "(" + (mIndex + 1) + "/" + mImageList.size() + ")";
        textView.setText(text);
    }

    private void setPanelVisible(boolean visible) {
        if (!mLocked) {
            mDialogView.findViewById(R.id.panel).setVisibility(visible ? View.VISIBLE : View.GONE);
        }
    }

    private void lockOrNot(boolean lock) {
        if (lock != mLocked) {
            if (lock) {
                setPanelVisible(false);
            }
            mLocked = lock;
            if (!lock) {
                setPanelVisible(true);
            }
        }
    }

    public void setImage(int imageId) {
        List<Integer> list = new ArrayList<Integer>();
        list.add(imageId);
        setImage(list);
    }

    public void setImage(List<Integer> imageIdList) {
        setImage(imageIdList, 0);
    }

    public void setImage(List<Integer> imageIdList, int displayPosition) {
        if (imageIdList != null) {
            mImageList = imageIdList;
        }
        if (mImageList.size() == 0) {
            mImageList.add(0);
        }
        //first display position
        if (displayPosition >= 0 && displayPosition < mImageList.size()) {
            mIndex = displayPosition;
        } else {
            mIndex = 0;
        }
        if (mImageList.size() == 1) {
            mDialogView.findViewById(R.id.position).setVisibility(View.GONE);
        }
        updateImageBitmap(UPDATE_IMAGE_LEFT);
        updateImageBitmap(UPDATE_IMAGE_CENTER);
        updateImageBitmap(UPDATE_IMAGE_RIGHT);
        updateCurrentPositionView();
    }

    public void setConfirmListener(ConfirmListener confirmListener) {
        mConfirmListener = confirmListener;
        if (mConfirmListener != null) {
            mDialogView.findViewById(R.id.confirm).setVisibility(View.VISIBLE);
        }
    }

    @SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.confirm:
                if (mConfirmListener != null) {
                    mConfirmListener.onConfirm(mIndex);
                }
                break;
            case R.id.lock:
                lockOrNot(true);
                break;
            default:
                break;
        }
    }
}